﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

namespace png2ico
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.button1.Click += Button1_Click;
        }

        private void Button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog openDialog = new OpenFileDialog();
            openDialog.Filter = "图片(*.png)|*.png";
            openDialog.Multiselect = false;
            openDialog.ShowDialog();
            if (openDialog.FileName != "")
            {
                using (Bitmap png = new Bitmap(openDialog.FileName))
                {
                    SaveFileDialog saveDialog = new SaveFileDialog();
                    saveDialog.Filter = "图标(*.ico)|*.ico";
                    saveDialog.CheckPathExists = true;
                    saveDialog.ShowDialog();
                    if (!string.IsNullOrEmpty(saveDialog.FileName))
                    {
                        IconFile iconFile = new IconFile();
                        iconFile.AddBmp(png, 16);
                        iconFile.AddBmp(png, 24);
                        iconFile.AddBmp(png, 32);
                        iconFile.AddBmp(png, 48);
                        iconFile.AddBmp(png, 64);
                        iconFile.AddBmp(png, 96);
                        iconFile.AddBmp(png, 128);
                        iconFile.AddPng(png, 256);
                        iconFile.Save(saveDialog.FileName);
                        MessageBox.Show("图标生成成功！");
                    }
                }
            }
        }
    }

    class IconFile
    {
        List<IconDirEntry> idEntries = new List<IconDirEntry>(); // 对应idCount的结构体，多少张图就有多少个该结构
        MemoryStream fileData = new MemoryStream();

        public void AddBmp(Image img, int size)
        {
            byte[] png;
            using (MemoryStream ms = new MemoryStream())
            {
                Bitmap bmp = new Bitmap(size, size, PixelFormat.Format32bppArgb);
                Graphics graphics = Graphics.FromImage(bmp);
                graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                graphics.DrawImage(img, new Rectangle(0, 0, size, size));
                graphics.Dispose();

                //Bitmap bmp = new Bitmap(img, new Size(size, size));
                new IconBitmap(bmp).ToStream(ms);
                png = ms.ToArray();
            }
            fileData.Write(png, 0, png.Length);

            IconDirEntry entry = new IconDirEntry();
            entry.bWidth = Convert.ToByte((size >= 256) ? 0 : size);
            entry.bHeight = entry.bWidth;
            entry.dwBytesInRes = Convert.ToUInt32(png.Length);
            entry.dwImageOffset = 0;
            idEntries.Add(entry);
        }

        public void AddPng(Image img, int size)
        {
            byte[] png;
            using (MemoryStream ms = new MemoryStream())
            {
                Bitmap bmp = new Bitmap(img, new Size(size, size));
                bmp.Save(ms, ImageFormat.Png);
                png = ms.ToArray();
            }
            fileData.Write(png, 0, png.Length);

            IconDirEntry entry = new IconDirEntry();
            entry.bWidth = Convert.ToByte((size >= 256) ? 0 : size);
            entry.bHeight = entry.bWidth;
            entry.dwBytesInRes = Convert.ToUInt32(png.Length);
            entry.dwImageOffset = 0;
            idEntries.Add(entry);
        }

        public void ToStream(Stream stream)
        {
            // 目录头
            IconHeader header = new IconHeader();
            header.idCount = Convert.ToUInt16(idEntries.Count);
            header.ToStream(stream);

            // 索引段
            uint startOffset = Convert.ToUInt32(6 + 16 * idEntries.Count);
            foreach (IconDirEntry entry in idEntries)
            {
                entry.dwImageOffset = startOffset;
                entry.ToStream(stream);
                startOffset += entry.dwBytesInRes;
            }

            // 图像数据段
            fileData.Position = 0;
            fileData.WriteTo(stream);
            fileData.Close();
        }

        public void Save(string filePath)
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Create))
            {
                this.ToStream(fs);
            }
        }
    }

    /// <summary>
    /// 目录头，6字节
    /// </summary>
    class IconHeader
    {
        public UInt16 idReserved = 0;  // 保留位必须为0
        public UInt16 idType = 1;      // 类型 (1=icon,2=光标)，也就是必须为1
        public UInt16 idCount = 0;     // 包含多少张图（icon可以含多张图，分别在不同尺寸下展示）

        public void ToStream(Stream stream)
        {
            stream.WriteByte(Convert.ToByte(idReserved & 0xff));
            stream.WriteByte(Convert.ToByte(idReserved >> 8));

            stream.WriteByte(Convert.ToByte(idType & 0xff));
            stream.WriteByte(Convert.ToByte(idType >> 8));

            stream.WriteByte(Convert.ToByte(idCount & 0xff));
            stream.WriteByte(Convert.ToByte(idCount >> 8));
        }
    };

    /// <summary>
    /// 目录头，16字节
    /// </summary>
    class IconDirEntry
    {
        public byte bWidth = 0;      // 图片的像素宽度
        public byte bHeight = 0;     // 图片的像素高度
        public byte bColorCount = 0; // 颜色深度：=1<<(wBitCount×wPlanes),如果大于等于8就为0；但是可以不管，填0就行
        public byte bReserved = 0;   // 保留位必须为零
        public UInt16 wPlanes = 1;     // Color Planes
        public UInt16 wBitCount = 32;   // Bits per pixel
        public UInt32 dwBytesInRes;  //该图字节数（位图头+image数据+掩码的字节总数）
        public UInt32 dwImageOffset; //相对于文件开始处的偏移量，单个图填22，毕竟icon头加起来22字节

        public void ToStream(Stream stream)
        {
            stream.WriteByte(bWidth);
            stream.WriteByte(bHeight);
            stream.WriteByte(bColorCount);
            stream.WriteByte(bReserved);

            stream.WriteByte(Convert.ToByte(wPlanes & 0xff));
            stream.WriteByte(Convert.ToByte((wPlanes >> 8) & 0xff));

            stream.WriteByte(Convert.ToByte(wBitCount & 0xff));
            stream.WriteByte(Convert.ToByte(wBitCount >> 8));

            stream.WriteByte(Convert.ToByte(dwBytesInRes & 0xff));
            stream.WriteByte(Convert.ToByte((dwBytesInRes >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((dwBytesInRes >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((dwBytesInRes >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(dwImageOffset & 0xff));
            stream.WriteByte(Convert.ToByte((dwImageOffset >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((dwImageOffset >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((dwImageOffset >> 24) & 0xff));
        }
    }

    class IconBitmap
    {
        // DIB header
        public UInt32 biSize = 40;//结构长度，40Byte
        public UInt32 biWidth; //图宽度
        public UInt32 biHeight;//图高度（xor+and的）也就是两倍高度
        public UInt16 biPlanes = 1;  // Color Planes
        public UInt16 biBitCount = 32; // Bits per pixel
        public UInt32 biCompression = 0;//压缩方式，填的0
        public UInt32 biSizeImage = 0;//图大小，仅rgba部分
        public UInt32 biXPelsPerMeter = 0;//（后面四个貌似不用管，是属于bmp的格式），X方向分辨率
        public UInt32 biYPelsPerMeter = 0;//Y方向分辨率
        public UInt32 biClrUsed = 0;//颜色数，可以填0默认
        public UInt32 biClrImportant = 0;//重要颜色数，填0表示全是重要

        //RGBQUAD[] icColors; // 颜色表，24/32色不用颜色表

        //AND掩码 XOR掩码   规则
        //  0	  n        显示icColors[n]指定的颜色
        //  1	  0	       透明
        //  1	  非0      屏幕反色
        byte[] icXOR;  // 填图片原始数据
        byte[] icAND;  // 24或32色填0

        public IconBitmap(Bitmap bmp)
        {
            biWidth = Convert.ToUInt32(bmp.Width);
            biHeight = Convert.ToUInt32(bmp.Height * 2);

            using (MemoryStream ms = new MemoryStream())
            {
                bmp.Save(ms, ImageFormat.Bmp);
                byte[] data = ms.ToArray(); // data包括:14字节文件头+40字节位图信息头+RGB颜色矩阵

                this.icXOR = new byte[data.Length - 14 - 40];
                Array.Copy(data, 14 + 40, icXOR, 0, icXOR.Length);
                biSizeImage = Convert.ToUInt32(this.icXOR.Length);

                this.icAND = new byte[this.icXOR.Length / 8];
                Array.Clear(this.icAND, 0, icAND.Length);
            }
        }

        public void ToStream(Stream stream)
        {
            stream.WriteByte(Convert.ToByte(biSize & 0xff));
            stream.WriteByte(Convert.ToByte((biSize >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biSize >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biSize >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biWidth & 0xff));
            stream.WriteByte(Convert.ToByte((biWidth >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biWidth >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biWidth >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biHeight & 0xff));
            stream.WriteByte(Convert.ToByte((biHeight >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biHeight >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biHeight >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biPlanes & 0xff));
            stream.WriteByte(Convert.ToByte((biPlanes >> 8) & 0xff));

            stream.WriteByte(Convert.ToByte(biBitCount & 0xff));
            stream.WriteByte(Convert.ToByte((biBitCount >> 8) & 0xff));

            stream.WriteByte(Convert.ToByte(biCompression & 0xff));
            stream.WriteByte(Convert.ToByte((biCompression >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biCompression >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biCompression >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biSizeImage & 0xff));
            stream.WriteByte(Convert.ToByte((biSizeImage >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biSizeImage >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biSizeImage >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biXPelsPerMeter & 0xff));
            stream.WriteByte(Convert.ToByte((biXPelsPerMeter >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biXPelsPerMeter >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biXPelsPerMeter >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biYPelsPerMeter & 0xff));
            stream.WriteByte(Convert.ToByte((biYPelsPerMeter >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biYPelsPerMeter >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biYPelsPerMeter >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biClrUsed & 0xff));
            stream.WriteByte(Convert.ToByte((biClrUsed >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biClrUsed >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biClrUsed >> 24) & 0xff));

            stream.WriteByte(Convert.ToByte(biClrImportant & 0xff));
            stream.WriteByte(Convert.ToByte((biClrImportant >> 8) & 0xff));
            stream.WriteByte(Convert.ToByte((biClrImportant >> 16) & 0xff));
            stream.WriteByte(Convert.ToByte((biClrImportant >> 24) & 0xff));

            stream.Write(icXOR, 0, icXOR.Length);
            stream.Write(icAND, 0, icAND.Length);
        }
    }
}
